

<!DOCTYPE html>
<html lang="zh-CN" data-default-color-scheme=dark>



<head>
  <meta charset="UTF-8">
  <link rel="apple-touch-icon" sizes="76x76" href="/my-blog/img/fluid.png">
  <link rel="icon" href="/my-blog/img/fluid.png">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, shrink-to-fit=no">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  
  <meta name="theme-color" content="#2f4154">
  <meta name="author" content="木舟">
  <meta name="keywords" content="python Vue Test 测试开发 自动化测试">
  
    <meta name="description" content="前言前面的知识，设计的主要是如何使用vue。 而组件分离，才是vue真正的核心。 可以说，前面我们学习的事件、指令等知识，都是为了更好的编写组件代码。 组件允许我们将 UI 划分为独立的、可重用的部分，并且可以对每个部分进行单独的思考。 在实际应用中，组件常常被组织成层层嵌套的树状结构：  定义组件使用如vite、webpack等打包工具时，我们一般会将单独的vue组件定义成一个.vue文件中，这">
<meta property="og:type" content="article">
<meta property="og:title" content="vue组件">
<meta property="og:url" content="https://walker_kwu.gitee.io/my-blog/2022/11/19/notes/vue/component/">
<meta property="og:site_name" content="My Blog">
<meta property="og:description" content="前言前面的知识，设计的主要是如何使用vue。 而组件分离，才是vue真正的核心。 可以说，前面我们学习的事件、指令等知识，都是为了更好的编写组件代码。 组件允许我们将 UI 划分为独立的、可重用的部分，并且可以对每个部分进行单独的思考。 在实际应用中，组件常常被组织成层层嵌套的树状结构：  定义组件使用如vite、webpack等打包工具时，我们一般会将单独的vue组件定义成一个.vue文件中，这">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://walker_kwu.gitee.io/my-blog/img/use/Vue%E7%BB%84%E4%BB%B6%E8%AE%BE%E8%AE%A1%E6%A6%82%E5%9B%BE.png">
<meta property="og:image" content="https://walker_kwu.gitee.io/my-blog/img/use/VueSlot.jpg">
<meta property="article:published_time" content="2022-11-19T05:00:07.000Z">
<meta property="article:modified_time" content="2023-05-23T10:37:53.260Z">
<meta property="article:author" content="木舟">
<meta property="article:tag" content="测试开发">
<meta property="article:tag" content="测试平台">
<meta property="article:tag" content="javascript">
<meta property="article:tag" content="Vue">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://walker_kwu.gitee.io/my-blog/img/use/Vue%E7%BB%84%E4%BB%B6%E8%AE%BE%E8%AE%A1%E6%A6%82%E5%9B%BE.png">
  
  
    <meta name="referrer" content="no-referrer-when-downgrade">
  
  
  <title>vue组件 - My Blog</title>

  <link  rel="stylesheet" href="https://lib.baomitu.com/twitter-bootstrap/4.6.1/css/bootstrap.min.css" />



  <link  rel="stylesheet" href="https://lib.baomitu.com/github-markdown-css/4.0.0/github-markdown.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/hint.css/2.7.0/hint.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.css" />



<!-- 主题依赖的图标库，不要自行修改 -->
<!-- Do not modify the link that theme dependent icons -->

<link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_hj8rtnfg7um.css">



<link rel="stylesheet" href="//at.alicdn.com/t/font_1736178_lbnruvf0jn.css">


<link  rel="stylesheet" href="/my-blog/css/main.css" />


  <link id="highlight-css" rel="stylesheet" href="/my-blog/css/highlight.css" />
  
    <link id="highlight-css-dark" rel="stylesheet" href="/my-blog/css/highlight-dark.css" />
  




  <script id="fluid-configs">
    var Fluid = window.Fluid || {};
    Fluid.ctx = Object.assign({}, Fluid.ctx)
    var CONFIG = {"hostname":"walker_kwu.gitee.io","root":"/my-blog/","version":"1.9.2","typing":{"enable":true,"typeSpeed":70,"cursorChar":"_","loop":false,"scope":[]},"anchorjs":{"enable":true,"element":"h1,h2,h3,h4,h5,h6","placement":"left","visible":"hover","icon":""},"progressbar":{"enable":true,"height_px":3,"color":"#29d","options":{"showSpinner":false,"trickleSpeed":100}},"code_language":{"enable":true,"default":"TEXT"},"copy_btn":true,"image_caption":{"enable":true},"image_zoom":{"enable":true,"img_url_replace":["",""]},"toc":{"enable":true,"placement":"left","headingSelector":"h1,h2,h3,h4,h5,h6","collapseDepth":0},"lazyload":{"enable":true,"loading_img":"/img/loading.gif","onlypost":false,"offset_factor":2},"web_analytics":{"enable":false,"follow_dnt":true,"baidu":null,"google":null,"gtag":null,"tencent":{"sid":null,"cid":null},"woyaola":null,"cnzz":null,"leancloud":{"app_id":null,"app_key":null,"server_url":null,"path":"window.location.pathname","ignore_local":false}},"search_path":"/my-blog/local-search.xml"};

    if (CONFIG.web_analytics.follow_dnt) {
      var dntVal = navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack;
      Fluid.ctx.dnt = dntVal && (dntVal.startsWith('1') || dntVal.startsWith('yes') || dntVal.startsWith('on'));
    }
  </script>
  <script  src="/my-blog/js/utils.js" ></script>
  <script  src="/my-blog/js/color-schema.js" ></script>
  


  
<meta name="generator" content="Hexo 6.2.0"></head>


<body>
  

  <header>
    

<div class="header-inner" style="height: 70vh;">
  <nav id="navbar" class="navbar fixed-top  navbar-expand-lg navbar-dark scrolling-navbar">
  <div class="container">
    <a class="navbar-brand" href="/my-blog/">
      <strong>木舟</strong>
    </a>

    <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <div class="animated-icon"><span></span><span></span><span></span></div>
    </button>

    <!-- Collapsible content -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto text-center">
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/my-blog/">
                <i class="iconfont icon-home-fill"></i>
                首页
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/my-blog/archives/">
                <i class="iconfont icon-archive-fill"></i>
                归档
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/my-blog/categories/">
                <i class="iconfont icon-category-fill"></i>
                分类
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/my-blog/tags/">
                <i class="iconfont icon-tags-fill"></i>
                标签
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/my-blog/about/">
                <i class="iconfont icon-user-fill"></i>
                关于
              </a>
            </li>
          
        
        
          <li class="nav-item" id="search-btn">
            <a class="nav-link" target="_self" href="javascript:;" data-toggle="modal" data-target="#modalSearch" aria-label="Search">
              &nbsp;<i class="iconfont icon-search"></i>&nbsp;
            </a>
          </li>
          
        
        
          <li class="nav-item" id="color-toggle-btn">
            <a class="nav-link" target="_self" href="javascript:;" aria-label="Color Toggle">&nbsp;<i
                class="iconfont icon-dark" id="color-toggle-icon"></i>&nbsp;</a>
          </li>
        
      </ul>
    </div>
  </div>
</nav>

  

<div id="banner" class="banner" parallax=true
     style="background: url('/my-blog/img/default.png') no-repeat center center; background-size: cover;">
  <div class="full-bg-img">
    <div class="mask flex-center" style="background-color: rgba(0, 0, 0, 0.3)">
      <div class="banner-text text-center fade-in-up">
        <div class="h2">
          
            <span id="subtitle" data-typed-text="vue组件"></span>
          
        </div>

        
          
  <div class="mt-3">
    
      <span class="post-meta mr-2">
        <i class="iconfont icon-author" aria-hidden="true"></i>
        木舟
      </span>
    
    
      <span class="post-meta">
        <i class="iconfont icon-date-fill" aria-hidden="true"></i>
        <time datetime="2022-11-19 13:00" pubdate>
          2022年11月19日 下午
        </time>
      </span>
    
  </div>

  <div class="mt-1">
    
      <span class="post-meta mr-2">
        <i class="iconfont icon-chart"></i>
        
          12k 字
        
      </span>
    

    

    
    
      
        <span id="busuanzi_container_page_pv" style="display: none">
          <i class="iconfont icon-eye" aria-hidden="true"></i>
          <span id="busuanzi_value_page_pv"></span> 次
        </span>
        
      
    
  </div>


        
      </div>

      
    </div>
  </div>
</div>

</div>

  </header>

  <main>
    
      

<div class="container-fluid nopadding-x">
  <div class="row nomargin-x">
    <div class="side-col d-none d-lg-block col-lg-2">
      
  <aside class="sidebar" style="padding-left: 2rem; margin-right: -1rem">
    <div id="toc">
  <p class="toc-header"><i class="iconfont icon-list"></i>&nbsp;目录</p>
  <div class="toc-body" id="toc-body"></div>
</div>



  </aside>


    </div>

    <div class="col-lg-8 nopadding-x-md">
      <div class="container nopadding-x-md" id="board-ctn">
        <div id="board">
          <article class="post-content mx-auto">
            <!-- SEO header -->
            <h1 style="display: none">vue组件</h1>
            
              <p class="note note-info">
                
                  
                    本文最后更新于：2023年5月23日 晚上
                  
                
              </p>
            
            
              <div class="markdown-body">
                
                <h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>前面的知识，设计的主要是如何使用vue。</p>
<p>而组件分离，才是vue真正的核心。</p>
<p>可以说，前面我们学习的事件、指令等知识，都是为了更好的编写组件代码。</p>
<p>组件允许我们将 UI 划分为独立的、可重用的部分，并且可以对每个部分进行单独的思考。</p>
<p>在实际应用中，组件常常被组织成层层嵌套的树状结构：</p>
<p><img src="/my-blog/img/use/Vue%E7%BB%84%E4%BB%B6%E8%AE%BE%E8%AE%A1%E6%A6%82%E5%9B%BE.png" srcset="/my-blog/img/loading.gif" lazyload></p>
<h1 id="定义组件"><a href="#定义组件" class="headerlink" title="定义组件"></a>定义组件</h1><p>使用如vite、webpack等打包工具时，我们一般会将单独的vue组件定义成一个<code>.vue</code>文件中，这个就叫 <strong>单文件组件</strong>。</p>
<p>但也有通过 <code>.js</code> 来创建组件的，但是这种方式，没有很好的代码提示，而且不适用于vite、webpack等打包工具.</p>
<h2 id="vue单文件组件"><a href="#vue单文件组件" class="headerlink" title=".vue单文件组件"></a>.vue单文件组件</h2><p>新建 Child1.vue</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs vue">&lt;template&gt;<br>	&lt;div&gt;<br>        我是Child1组件<br>	&lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;script setup&gt;<br><br>&lt;/script&gt;<br><br>&lt;style scoped&gt;<br><br>&lt;/style&gt;<br></code></pre></td></tr></table></figure>

<ul>
<li>template，编写dom元素，就是html代码</li>
<li>script，编写js业务逻辑</li>
<li>style，编写css代码。如果加上 <strong>scoped</strong> 关键字，代表 组件的css代码，只能生效于该组件。</li>
</ul>
<h2 id="js单文件组件"><a href="#js单文件组件" class="headerlink" title=".js单文件组件"></a>.js单文件组件</h2><p>该方法也无法使用 <code>&lt;style scoped&gt;</code></p>
<p>新建 Child2.js</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> &#123;<br>  <span class="hljs-title function_">setup</span>(<span class="hljs-params"></span>) &#123;<br>  &#125;,<br>  <span class="hljs-attr">template</span>: <span class="hljs-string">`</span><br><span class="hljs-string">    &lt;div&gt;</span><br><span class="hljs-string">        我是Child2组件</span><br><span class="hljs-string">    &lt;/div&gt;`</span><br>&#125;<br></code></pre></td></tr></table></figure>


<h1 id="使用组件"><a href="#使用组件" class="headerlink" title="使用组件"></a>使用组件</h1><p>在 <code>App.vue</code>中导入我们新建的 <code>Child1.vue</code></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs vue">&lt;template&gt;<br>	&lt;h1&gt;下面有请Child1组件&lt;/h1&gt;<br>	&lt;Child1&gt;&lt;/Child1&gt;<br>	&lt;Child1/&gt;<br>&lt;/template&gt;<br><br>&lt;script setup&gt;<br>import Child1 from &#x27;./components/Child1.vue&#x27;<br>&lt;/script&gt;<br><br>&lt;style lang=&quot;scss&quot; scoped&gt;<br><br>&lt;/style&gt;<br></code></pre></td></tr></table></figure>
<p>通过 <code>&lt;script setup&gt;</code>，导入的组件都在模板中直接可用。</p>
<p>如果没有使用 <code>&lt;script setup&gt;</code>，则需要使用 components 选项来显式注册：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">import</span> <span class="hljs-title class_">ComponentA</span> <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./ComponentA.js&#x27;</span><br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> &#123;<br>  <span class="hljs-attr">components</span>: &#123;<br>    <span class="hljs-title class_">ComponentA</span>,<br>    <span class="hljs-title class_">ComponentB</span>: <span class="hljs-title class_">ComponentA</span><br>  &#125;,<br>  <span class="hljs-title function_">setup</span>(<span class="hljs-params"></span>) &#123;<br>    <span class="hljs-comment">// ...</span><br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>并且组件可以被重用多次。</p>
<p>注意：</p>
<blockquote>
<p>组件的命名、导入、使用，都要按照 <strong>大驼峰</strong> 的命名方式来使用。<br>即每个单词首字母都需要大写<br>这么做的目的是为了与 html 标签区分，并且 html 是不区分大小写的。<br>而Vue在某些场景下，会区分大小写。<br>当然你可以使用 <strong>全小写</strong> 写法，但必须使用 <code>-</code> 隔开。<br><code>&lt;MyComponent&gt;</code> -&gt; <code>&lt;my-component&gt;</code></p>
</blockquote>
<h1 id="全局注册与使用"><a href="#全局注册与使用" class="headerlink" title="全局注册与使用"></a>全局注册与使用</h1><p>上面我们介绍的都是 <strong>局部注册与使用</strong></p>
<p>当然，你也可以全局地注册一个组件，使得它在当前应用中的任何组件上都可以使用，而不需要额外再导入。</p>
<h2 id="全局注册"><a href="#全局注册" class="headerlink" title="全局注册"></a>全局注册</h2><p>我们可以使用 Vue 应用实例的 <code>app.component()</code> 方法，让组件在当前 Vue 应用中全局可用。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">import</span> &#123; createApp &#125; <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;vue&#x27;</span><br><br><span class="hljs-keyword">const</span> app = <span class="hljs-title function_">createApp</span>(&#123;&#125;)<br><br><span class="hljs-comment">/*</span><br><span class="hljs-comment">app.component(&#x27;MyComponent&#x27;, &#123;</span><br><span class="hljs-comment">    props: [&#x27;title&#x27;],</span><br><span class="hljs-comment">    setup()&#123;</span><br><span class="hljs-comment"></span><br><span class="hljs-comment">    &#125;,</span><br><span class="hljs-comment">    template: `</span><br><span class="hljs-comment">        &lt;div&gt;组件&lt;/div&gt;</span><br><span class="hljs-comment">    `,</span><br><span class="hljs-comment">&#125;)</span><br><span class="hljs-comment">*/</span><br><br>app.<span class="hljs-title function_">component</span>(<br>  <span class="hljs-comment">// 注册的名字</span><br>  <span class="hljs-string">&#x27;MyComponent&#x27;</span>,<br>  <span class="hljs-comment">// 组件的实现</span><br>  &#123;<br>    <span class="hljs-attr">props</span>: [<span class="hljs-string">&#x27;title&#x27;</span>],<br>    <span class="hljs-title function_">setup</span>(<span class="hljs-params"></span>)&#123;<br><br>    &#125;,<br>    <span class="hljs-attr">template</span>: <span class="hljs-string">`</span><br><span class="hljs-string">        &lt;div&gt;组件&lt;/div&gt;</span><br><span class="hljs-string">    `</span>,<br>  &#125;<br>)<br></code></pre></td></tr></table></figure>

<h2 id="如何使用"><a href="#如何使用" class="headerlink" title="如何使用"></a>如何使用</h2><p>如果使用单文件组件，你可以注册被导入的 <code>.vue</code> 文件：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">import</span> <span class="hljs-title class_">MyComponent</span> <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./MyComponent.vue&#x27;</span><br><br>app.<span class="hljs-title function_">component</span>(<span class="hljs-string">&#x27;MyComponent&#x27;</span>, <span class="hljs-title class_">MyComponent</span>)<br></code></pre></td></tr></table></figure>

<p><code>app.component()</code> 方法可以被链式调用：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs javascript">app<br>  .<span class="hljs-title function_">component</span>(<span class="hljs-string">&#x27;ComponentA&#x27;</span>, <span class="hljs-title class_">ComponentA</span>)<br>  .<span class="hljs-title function_">component</span>(<span class="hljs-string">&#x27;ComponentB&#x27;</span>, <span class="hljs-title class_">ComponentB</span>)<br>  .<span class="hljs-title function_">component</span>(<span class="hljs-string">&#x27;ComponentC&#x27;</span>, <span class="hljs-title class_">ComponentC</span>)<br></code></pre></td></tr></table></figure>

<p>全局注册的组件可以在此应用的任意组件的模板中使用：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs template">&lt;!-- 这在当前应用的任意组件中都可用 --&gt;<br>&lt;ComponentA/&gt;<br>&lt;ComponentB/&gt;<br>&lt;ComponentC/&gt;<br></code></pre></td></tr></table></figure>

<h2 id="VS局部注册"><a href="#VS局部注册" class="headerlink" title="VS局部注册"></a>VS局部注册</h2><p>全局注册虽然很方便，但有以下几个问题：</p>
<ul>
<li>全局注册，但并没有被使用的组件无法在生产打包时被自动移除 (。如果你全局注册了一个组件，即使它并没有被实际使用，它仍然会出现在打包后的 JS 文件中。</li>
<li>全局注册在大型项目中使项目的依赖关系变得不那么明确。在父组件中使用子组件时，不太容易定位子组件的实现。和使用过多的全局变量一样，这可能会影响应用长期的可维护性。</li>
</ul>
<h1 id="传递props-父向子传参"><a href="#传递props-父向子传参" class="headerlink" title="传递props(父向子传参)"></a>传递props(父向子传参)</h1><blockquote>
<p>props 的定义，还会影响到 <strong>透传</strong>，<strong>透传</strong>会有专门的章节进行介绍</p>
</blockquote>
<h2 id="如何定义"><a href="#如何定义" class="headerlink" title="如何定义"></a>如何定义</h2><p>如果我们正在构建一个博客，我们可能需要一个表示博客文章的组件。</p>
<p>我们希望所有的博客文章分享相同的视觉布局，但有不同的内容。</p>
<p>要实现这样的效果自然必须向组件中传递数据，例如每篇文章标题和内容，这就会使用到 props。</p>
<p>Props 是一种特别的 attributes，你可以在组件上声明注册。</p>
<p>要传递给博客文章组件一个标题，我们必须在组件的 props 列表上声明它。这里要用到 <code>defineProps</code> 宏：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs vue">&lt;script setup&gt;<br>defineProps([&#x27;title&#x27;])<br>&lt;/script&gt;<br><br>&lt;template&gt;<br>  &lt;h4 :title=&quot;title&quot;&gt;&#123;&#123; title &#125;&#125;&lt;/h4&gt;<br>&lt;/template&gt;<br></code></pre></td></tr></table></figure>

<p>defineProps 是一个仅 <code>&lt;script setup&gt;</code> 中可用的编译宏命令，并不需要显式地导入。</p>
<p>也就是不需要 <code>import defineProps from xxx</code> 直接使用即可</p>
<p>声明的 props 会自动暴露给模板。defineProps 会返回一个对象，其中包含了可以传递给组件的所有 props：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> props = <span class="hljs-title function_">defineProps</span>([<span class="hljs-string">&#x27;title&#x27;</span>])<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(props.<span class="hljs-property">title</span>)<br></code></pre></td></tr></table></figure>

<p>如果你没有使用 <code>&lt;script setup&gt;</code>，props 必须以 props 选项的方式声明，props 对象会作为 <code>setup()</code> 函数的第一个参数被传入：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> &#123;<br>  <span class="hljs-attr">props</span>: [<span class="hljs-string">&#x27;title&#x27;</span>],<br>  <span class="hljs-title function_">setup</span>(<span class="hljs-params">props</span>) &#123;<br>    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(props.<span class="hljs-property">title</span>)<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<h2 id="props设置类型"><a href="#props设置类型" class="headerlink" title="props设置类型"></a>props设置类型</h2><p>一个组件可以有任意多的 props，默认情况下，所有 prop 都接受任意类型的值。</p>
<p>当一个 prop 被注册后，可以像这样以自定义 attribute 的形式传递数据给它：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs template">&lt;BlogPost title=&quot;My journey with Vue&quot; /&gt;<br>&lt;BlogPost title=&quot;Blogging with Vue&quot; /&gt;<br>&lt;BlogPost title=&quot;Why Vue is so fun&quot; /&gt;<br></code></pre></td></tr></table></figure>

<p>在实际应用中，我们可能在父组件中会有如下的一个博客文章数组：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> posts = <span class="hljs-title function_">ref</span>([<br>  &#123; <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">&#x27;My journey with Vue&#x27;</span> &#125;,<br>  &#123; <span class="hljs-attr">id</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">&#x27;Blogging with Vue&#x27;</span> &#125;,<br>  &#123; <span class="hljs-attr">id</span>: <span class="hljs-number">3</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">&#x27;Why Vue is so fun&#x27;</span> &#125;<br>])<br></code></pre></td></tr></table></figure>

<p>这种情况下，我们可以使用 <code>v-for</code> 来渲染它们：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs template">&lt;BlogPost<br>  v-for=&quot;post in posts&quot;<br>  :key=&quot;post.id&quot;<br>  :title=&quot;post.title&quot;<br> /&gt;<br></code></pre></td></tr></table></figure>

<p>除了使用字符串数组来声明 prop 外，还可以使用对象的形式：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-title function_">defineProps</span>(&#123;<br>  <span class="hljs-attr">title</span>: <span class="hljs-title class_">String</span>,<br>  <span class="hljs-attr">likes</span>: <span class="hljs-title class_">Number</span><br>&#125;)<br><br><span class="hljs-comment">// 非 &lt;script setup&gt;</span><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> &#123;<br>  <span class="hljs-attr">props</span>: &#123;<br>    <span class="hljs-attr">title</span>: <span class="hljs-title class_">String</span>,<br>    <span class="hljs-attr">likes</span>: <span class="hljs-title class_">Number</span><br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>这个时候，如果传递的props的值类型与定义的不同，Vue会抛出警告。</p>
<h2 id="props校验"><a href="#props校验" class="headerlink" title="props校验"></a>props校验</h2><p>Vue 组件可以更细致地声明对传入的 props 的校验要求。</p>
<p>比如我们上面已经看到过的类型声明，如果传入的值不满足类型要求，Vue 会在浏览器控制台中抛出警告来提醒使用者。</p>
<p>这在开发给其他开发者使用的组件时非常有用。</p>
<p>要声明对 props 的校验，你可以向 defineProps() 宏提供一个带有 props 校验选项的对象，例如：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-title function_">defineProps</span>(&#123;<br>    <span class="hljs-attr">propA</span>: &#123;<br>        <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span>,<br>        <span class="hljs-attr">default</span>: <span class="hljs-string">&#x27;abc&#x27;</span>,<br>        <span class="hljs-title function_">default</span>(<span class="hljs-params">value</span>)&#123;<br>            <span class="hljs-keyword">return</span> <span class="hljs-string">&#x27;cba&#x27;</span><br>        &#125;,<br>        <span class="hljs-attr">type</span>: <span class="hljs-title class_">String</span>,<br>        <span class="hljs-title function_">validator</span>(<span class="hljs-params">value</span>)&#123;<br>            <span class="hljs-keyword">return</span> [<span class="hljs-string">&#x27;success&#x27;</span>, <span class="hljs-string">&#x27;warning&#x27;</span>, <span class="hljs-string">&#x27;danger&#x27;</span>].<span class="hljs-title function_">includes</span>(value)<br>        &#125;,<br>    &#125;<br>&#125;)<br></code></pre></td></tr></table></figure>

<h2 id="动态props"><a href="#动态props" class="headerlink" title="动态props"></a>动态props</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs template">&lt;!-- 根据一个变量的值动态传入 --&gt;<br>&lt;BlogPost :title=&quot;post.title&quot; /&gt;<br></code></pre></td></tr></table></figure>

<p><code>post.title</code> 一旦发生改变，那么 子组件中的 title 也会随着改变。</p>
<p><strong>props可以接收任意类型的值</strong></p>
<h2 id="单向数据流"><a href="#单向数据流" class="headerlink" title="单向数据流"></a>单向数据流</h2><p>所有的 props 都遵循着单向绑定原则，props 因父组件的更新而变化，自然地将新的状态向下流往子组件，而不会逆向传递。</p>
<p>这避免了子组件意外修改父组件的状态的情况，不然应用的数据流将很容易变得混乱而难以理解。</p>
<p>另外，每次父组件更新后，所有的子组件中的 props 都会被更新到最新值，这意味着你不应该在子组件中去更改一个 prop。</p>
<p>若你这么做了，Vue 会在控制台上向你抛出警告：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> props = <span class="hljs-title function_">defineProps</span>([<span class="hljs-string">&#x27;foo&#x27;</span>])<br><br><span class="hljs-comment">// ❌ 警告！prop 是只读的！</span><br>props.<span class="hljs-property">foo</span> = <span class="hljs-string">&#x27;bar&#x27;</span><br></code></pre></td></tr></table></figure>

<p>导致你想要更改一个 prop 的需求通常来源于以下两种场景：</p>
<ul>
<li>prop 被用于传入初始值；而子组件想在之后将其作为一个局部数据属性。</li>
</ul>
<p>在这种情况下，最好是新定义一个局部数据属性，从 props 上获取初始值即可：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> props = <span class="hljs-title function_">defineProps</span>([<span class="hljs-string">&#x27;initialCounter&#x27;</span>])<br><br><span class="hljs-comment">// 计数器只是将 props.initialCounter 作为初始值</span><br><span class="hljs-comment">// 像下面这样做就使 prop 和后续更新无关了</span><br><span class="hljs-keyword">const</span> counter = <span class="hljs-title function_">ref</span>(props.<span class="hljs-property">initialCounter</span>)<br></code></pre></td></tr></table></figure>

<ul>
<li>需要对传入的 prop 值做进一步的转换。在这种情况中，最好是基于该 prop 值定义一个计算属性：</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> props = <span class="hljs-title function_">defineProps</span>([<span class="hljs-string">&#x27;size&#x27;</span>])<br><br><span class="hljs-comment">// 该 prop 变更时计算属性也会自动更新</span><br><span class="hljs-keyword">const</span> normalizedSize = <span class="hljs-title function_">computed</span>(<span class="hljs-function">() =&gt;</span> props.<span class="hljs-property">size</span>.<span class="hljs-title function_">trim</span>().<span class="hljs-title function_">toLowerCase</span>())<br></code></pre></td></tr></table></figure>

<p><strong>更改对象 / 数组类型的 props</strong></p>
<p>当对象或数组作为 props 被传入时，虽然子组件无法更改 props 绑定，但仍然可以更改对象或数组内部的值。这是因为 JavaScript 的对象和数组是按引用传递，而对 Vue 来说，禁止这样的改动虽然可能，但有很大的性能损耗，比较得不偿失。</p>
<p>这种更改的主要缺陷是它允许了子组件以某种不明显的方式影响父组件的状态，可能会使数据流在将来变得更难以理解。在最佳实践中，你应该尽可能避免这样的更改，除非父子组件在设计上本来就需要紧密耦合。在大多数场景下，子组件应该 <strong>抛出一个事件</strong> 来通知父组件做出改变。</p>
<p><strong>抛出一个事件</strong> 需要借助 <strong>emit</strong> 来实现，而 <code>props + emit</code> 就是很经典的 <strong>父子通信</strong></p>
<h1 id="父子监听事件"><a href="#父子监听事件" class="headerlink" title="父子监听事件"></a>父子监听事件</h1><p>我们之前说过，Vue中的事件，都是通过 <code>v-on</code> 来监听的</p>
<p>相当于是JavaScript中的 onclick。</p>
<p>被监听的对象，只能是子元素。</p>
<p>那么，子组件是否可以被监听，如果可以，那又是如何被监听的。</p>
<h2 id="入门示例"><a href="#入门示例" class="headerlink" title="入门示例"></a>入门示例</h2><p>新建子组件 <code>Child.vue</code></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs vue">&lt;template&gt;<br>	&lt;div&gt;<br>		&#123;&#123;title&#125;&#125;<br>	&lt;/div&gt;<br>	&lt;button @click=&quot;$emit(&#x27;excite&#x27;)&quot;&gt;强调一下&lt;/button&gt;<br>&lt;/template&gt;<br><br>&lt;script setup&gt;<br>defineProps([&#x27;title&#x27;])<br>defineEmits([&#x27;excite&#x27;])<br>&lt;/script&gt;<br><br>&lt;style lang=&quot;scss&quot; scoped&gt;<br><br>&lt;/style&gt;<br></code></pre></td></tr></table></figure>

<p><code>App.vue</code></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs vue">&lt;template&gt;<br>	&lt;h1&gt;下面有请child1组件&lt;/h1&gt;<br>	&lt;Child :title=&quot;title&quot; @excite=&quot;title += &#x27;!&#x27;&quot;&gt;&lt;/Child&gt;<br>&lt;/template&gt;<br><br>&lt;script setup&gt;<br>import &#123; ref &#125; from &#x27;vue&#x27;;<br>import Child from &#x27;./components/Child.vue&#x27;<br>const title = ref(&#x27;这是Child组件&#x27;)<br>&lt;/script&gt;<br><br>&lt;style lang=&quot;scss&quot; scoped&gt;<br><br>&lt;/style&gt;<br></code></pre></td></tr></table></figure>

<p>在子组件中，通过 <code>defineEmits</code> 来声明可以被监听的事件</p>
<p>在父组件中，进行事件监听，当事件触发时，做出相对应的处理。</p>
<p>上述代码，当按钮被点击时，title后会新增一个感叹号。</p>
<h2 id="在JavaScript逻辑中使用"><a href="#在JavaScript逻辑中使用" class="headerlink" title="在JavaScript逻辑中使用"></a>在JavaScript逻辑中使用</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> emit = <span class="hljs-title function_">defineEmits</span>([<span class="hljs-string">&#x27;excite&#x27;</span>])<br><br><span class="hljs-keyword">const</span> <span class="hljs-title function_">onClick</span> = (<span class="hljs-params"></span>)=&gt;&#123;<br>    <span class="hljs-title function_">emit</span>(<span class="hljs-string">&#x27;excite&#x27;</span>)<br>&#125;<br></code></pre></td></tr></table></figure>

<h2 id="增加参数"><a href="#增加参数" class="headerlink" title="增加参数"></a>增加参数</h2><p>子组件：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> emit = <span class="hljs-title function_">defineEmits</span>([<span class="hljs-string">&#x27;excite&#x27;</span>])<br><br><span class="hljs-keyword">const</span> <span class="hljs-title function_">onClick</span> = (<span class="hljs-params"></span>)=&gt;&#123;<br>    <span class="hljs-title function_">emit</span>(<span class="hljs-string">&#x27;excite&#x27;</span>, <span class="hljs-string">&#x27;abc&#x27;</span>)<br>&#125;<br></code></pre></td></tr></table></figure>

<p>父组件：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs vue">&lt;template&gt;<br>    &lt;Child @excite=&quot;(value)=&gt;func(value)&quot;&gt;&lt;/Child&gt;<br>    &lt;Child @excite=&quot;func&quot;&gt;&lt;/Child&gt;<br>    &lt;Child @excite=&quot;(value)=&gt;func2(value, &#x27;new&#x27;)&quot;&gt;&lt;/Child&gt;<br>&lt;/template&gt;<br><br>&lt;script setup&gt;<br><br>const func = (value)=&gt;&#123;<br><br>&#125;<br><br>const func2 = (value, newValue)=&gt;&#123;<br><br>&#125;<br>&lt;/script&gt;<br></code></pre></td></tr></table></figure>

<h2 id="多个参数"><a href="#多个参数" class="headerlink" title="多个参数"></a>多个参数</h2><p>子组件：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> emit = <span class="hljs-title function_">defineEmits</span>([<span class="hljs-string">&#x27;excite&#x27;</span>])<br><br><span class="hljs-keyword">const</span> <span class="hljs-title function_">onClick</span> = (<span class="hljs-params"></span>)=&gt;&#123;<br>    <span class="hljs-title function_">emit</span>(<span class="hljs-string">&#x27;excite&#x27;</span>, <span class="hljs-string">&#x27;abc&#x27;</span>, <span class="hljs-string">&#x27;cba&#x27;</span>)<br>&#125;<br></code></pre></td></tr></table></figure>
<p>父组件：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs vue">&lt;template&gt;<br>    &lt;Child @excite=&quot;(value1, value2)=&gt;func(value1, value2)&quot;&gt;&lt;/Child&gt;<br>    &lt;Child @excite=&quot;func&quot;&gt;&lt;/Child&gt;<br>    &lt;Child @excite=&quot;(value1, value2)=&gt;func2(value1, value2, &#x27;new&#x27;)&quot;&gt;&lt;/Child&gt;<br>&lt;/template&gt;<br><br>&lt;script setup&gt;<br><br>const func = (value1, value2)=&gt;&#123;<br><br>&#125;<br><br>const func2 = (value1, value2, newValue)=&gt;&#123;<br><br>&#125;<br>&lt;/script&gt;<br></code></pre></td></tr></table></figure>

<h2 id="事件校验"><a href="#事件校验" class="headerlink" title="事件校验"></a>事件校验</h2><p>/</p>
<blockquote>
<p>使用次数较少，有兴趣可自行查看官网例子。此处不做讲解<br><a target="_blank" rel="noopener" href="https://cn.vuejs.org/guide/components/events.html#events-validation">https://cn.vuejs.org/guide/components/events.html#events-validation</a></p>
</blockquote>
<h1 id="动态组件"><a href="#动态组件" class="headerlink" title="动态组件"></a>动态组件</h1><p>有些场景会需要在两个组件间来回切换，比如 Tab 界面：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs template">&lt;component :is=&quot;tabs[currentTab]&quot;&gt;&lt;/component&gt;<br></code></pre></td></tr></table></figure>

<p>在上面的例子中，被传给 :is 的值可以是以下几种：</p>
<ul>
<li>被注册的组件名</li>
<li>导入的组件对象</li>
</ul>
<p>当使用 <code>&lt;component :is=&quot;...&quot;&gt;</code> 来在多个组件间作切换时，被切换掉的组件会被卸载。</p>
<p>我们可以通过 <code>&lt;KeepAlive&gt;</code> 组件强制被切换掉的组件仍然保持“存活”的状态。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs vue">&lt;template&gt;<br>	&lt;component :is=&quot;componentConfig[comKey]&quot;&gt;&lt;/component&gt;<br>&lt;/template&gt;<br><br>&lt;script setup&gt;<br>import &#123; reactive &#125; from &#x27;vue&#x27;;<br>import Child1 from &#x27;./components/Child.vue&#x27;<br>import Child2 from &#x27;./components/Child.vue&#x27;<br><br>const comKey = ref(&#x27;child1&#x27;)<br><br>const componentConfig = reactive(&#123;<br>	child1: Child1,<br>	child2: Child2,<br>&#125;)<br><br><br>&lt;/script&gt;<br><br>&lt;style lang=&quot;scss&quot; scoped&gt;<br><br>&lt;/style&gt;<br></code></pre></td></tr></table></figure>

<p>这个时候，只要修改 <code>comKey</code> 为对应的 key，则可以进行组件切换</p>
<h2 id="KeepAlive"><a href="#KeepAlive" class="headerlink" title="KeepAlive"></a>KeepAlive</h2><p>详见官网例子</p>
<blockquote>
<p><a target="_blank" rel="noopener" href="https://cn.vuejs.org/guide/built-ins/keep-alive.html">https://cn.vuejs.org/guide/built-ins/keep-alive.html</a></p>
</blockquote>
<h1 id="插槽"><a href="#插槽" class="headerlink" title="插槽"></a>插槽</h1><p>一些情况下我们会希望组件能和 HTML 元素一样向组件中传递内容：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs template">&lt;AlertBox&gt;<br>  Something bad happened.<br>&lt;/AlertBox&gt;<br></code></pre></td></tr></table></figure>

<p>我们期望能渲染成这样：</p>
<p><img src="/my-blog/img/use/VueSlot.jpg" srcset="/my-blog/img/loading.gif" lazyload></p>
<p>这可以通过 Vue 的自定义 <code>&lt;slot&gt;</code> 元素来实现：</p>
<p>AlertBox.vue 组件：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs vue">&lt;template&gt;<br>  &lt;div class=&quot;alert-box&quot;&gt;<br>    &lt;strong&gt;This is an Error for Demo Purposes&lt;/strong&gt;<br>    &lt;slot /&gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;style scoped&gt;<br>.alert-box &#123;<br>  /* ... */<br>&#125;<br>&lt;/style&gt;<br></code></pre></td></tr></table></figure>

<p>如上所示，我们使用 <code>&lt;slot&gt;</code> 作为一个占位符，父组件传递进来的内容就会渲染在这里。</p>
<p>插槽是Vue的核心之一，插槽能够帮助你更好的封装一个共用组件。</p>
<p>核心的概念便是 <strong>模板化</strong>。</p>
<p>通过制定模板，然后以插槽来插入自定义内容。</p>
<p>使得模板组件更加灵活且具有可复用性。</p>
<h2 id="默认插槽"><a href="#默认插槽" class="headerlink" title="默认插槽"></a>默认插槽</h2><p>在外部没有提供任何内容的情况下，可以为插槽指定默认内容。比如有这样一个 <code>&lt;SubmitButton&gt;</code> 组件：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs template">&lt;button type=&quot;submit&quot;&gt;<br>  &lt;slot&gt;&lt;/slot&gt;<br>&lt;/button&gt;<br></code></pre></td></tr></table></figure>

<p>如果我们想在父组件没有提供任何插槽内容时在 <code>&lt;button&gt;</code> 内渲染“Submit”，只需要将“Submit”写在 <code>&lt;slot&gt;</code> 标签之间来作为默认内容：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs template">&lt;button type=&quot;submit&quot;&gt;<br>  &lt;slot&gt;<br>    Submit &lt;!-- 默认内容 --&gt;<br>  &lt;/slot&gt;<br>&lt;/button&gt;<br></code></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs template">&lt;SubmitButton&gt;&lt;/SubmitButton&gt;<br>&lt;SubmitButton/&gt;<br>&lt;SubmitButton&gt;Save&lt;/SubmitButton&gt;<br></code></pre></td></tr></table></figure>

<p>相当于渲染：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs template">&lt;button type=&quot;submit&quot;&gt;Submit&lt;/button&gt;<br>&lt;button type=&quot;submit&quot;&gt;Submit&lt;/button&gt;<br>&lt;button type=&quot;submit&quot;&gt;Save&lt;/button&gt;<br></code></pre></td></tr></table></figure>

<h2 id="具名插槽"><a href="#具名插槽" class="headerlink" title="具名插槽"></a>具名插槽</h2><p>有时候，我们的模板组件，会不同的地方配置不同的插槽口。</p>
<p>来达到模板更高度的自定义效果，但使用时，需要指定插槽口名称。</p>
<h3 id="如何定义-1"><a href="#如何定义-1" class="headerlink" title="如何定义"></a>如何定义</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs template">&lt;div class=&quot;container&quot;&gt;<br>  &lt;header&gt;<br>    &lt;slot name=&quot;header&quot;&gt;&lt;/slot&gt;<br>  &lt;/header&gt;<br>  &lt;main&gt;<br>    &lt;slot&gt;&lt;/slot&gt;<br>  &lt;/main&gt;<br>  &lt;footer&gt;<br>    &lt;slot name=&quot;footer&quot;&gt;&lt;/slot&gt;<br>  &lt;/footer&gt;<br>&lt;/div&gt;<br></code></pre></td></tr></table></figure>

<p>这类带 <code>name</code> 的插槽被称为具名插槽 (named slots)。没有提供 <code>name</code> 的 <code>&lt;slot&gt;</code> 出口会隐式地命名为“default”。</p>
<h3 id="如何使用-1"><a href="#如何使用-1" class="headerlink" title="如何使用"></a>如何使用</h3><p>要为具名插槽传入内容，我们需要使用一个含 v-slot 指令的 <code>&lt;template&gt;</code> 元素，并将目标插槽的名字传给该指令：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs template">&lt;BaseLayout&gt;<br>  &lt;template v-slot:header&gt;<br>    &lt;h1&gt;Here might be a page title&lt;/h1&gt;<br>  &lt;/template&gt;<br><br>  &lt;template #default&gt;<br>    &lt;p&gt;A paragraph for the main content.&lt;/p&gt;<br>    &lt;p&gt;And another one.&lt;/p&gt;<br>  &lt;/template&gt;<br><br>  &lt;template #footer&gt;<br>    &lt;p&gt;Here&#x27;s some contact info&lt;/p&gt;<br>  &lt;/template&gt;<br>&lt;/BaseLayout&gt;<br></code></pre></td></tr></table></figure>

<p>v-slot 有对应的简写 <code>#</code>，因此 <code>&lt;template v-slot:header&gt;</code> 可以简写为 <code>&lt;template #header&gt;</code>。</p>
<p>其意思就是“将这部分模板片段传入子组件的 <code>header</code> 插槽中”。</p>
<p>当一个组件同时接收默认插槽和具名插槽时，所有位于顶级的非 <code>&lt;template&gt;</code> 节点都被隐式地视为默认插槽的内容。所以上面也可以写成：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs template">&lt;BaseLayout&gt;<br>  &lt;template #header&gt;<br>    &lt;h1&gt;Here might be a page title&lt;/h1&gt;<br>  &lt;/template&gt;<br><br>  &lt;!-- 隐式的默认插槽 --&gt;<br>  &lt;p&gt;A paragraph for the main content.&lt;/p&gt;<br>  &lt;p&gt;And another one.&lt;/p&gt;<br><br>  &lt;template #footer&gt;<br>    &lt;p&gt;Here&#x27;s some contact info&lt;/p&gt;<br>  &lt;/template&gt;<br>&lt;/BaseLayout&gt;<br></code></pre></td></tr></table></figure>

<h2 id="动态插槽名"><a href="#动态插槽名" class="headerlink" title="动态插槽名"></a>动态插槽名</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs template">&lt;base-layout&gt;<br>  &lt;template v-slot:[dynamicSlotName]&gt;<br>    ...<br>  &lt;/template&gt;<br><br>  &lt;!-- 缩写为 --&gt;<br>  &lt;template #[dynamicSlotName]&gt;<br>    ...<br>  &lt;/template&gt;<br>&lt;/base-layout&gt;<br></code></pre></td></tr></table></figure>

<h2 id="作用域插槽"><a href="#作用域插槽" class="headerlink" title="作用域插槽"></a>作用域插槽</h2><p>在某些场景下插槽的内容可能想要同时使用父组件域内和子组件域内的数据。</p>
<p>要做到这一点，我们需要一种方法来让子组件在渲染时将一部分数据提供给插槽。</p>
<p>我们也确实有办法这么做！可以像对组件传递 props 那样，向一个插槽的出口上传递 attributes：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs template">&lt;!-- &lt;MyComponent&gt; 的模板 --&gt;<br>&lt;div&gt;<br>  &lt;slot :text=&quot;greetingMessage&quot; :count=&quot;1&quot;&gt;&lt;/slot&gt;<br>&lt;/div&gt;<br></code></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs template">&lt;MyComponent v-slot=&quot;slotProps&quot;&gt;<br>  &#123;&#123; slotProps.text &#125;&#125; &#123;&#123; slotProps.count &#125;&#125;<br>&lt;/MyComponent&gt;<br>&lt;MyComponent v-slot=&quot;slotProps&quot;&gt;<br>  &#123;&#123; slotProps.text &#125;&#125; &#123;&#123; slotProps.count &#125;&#125;<br>&lt;/MyComponent&gt;<br></code></pre></td></tr></table></figure>

<h3 id="具名作用域插槽"><a href="#具名作用域插槽" class="headerlink" title="具名作用域插槽"></a>具名作用域插槽</h3><p>具名作用域插槽的工作方式也是类似的，插槽 <code>props</code> 可以作为 <code>v-slot</code> 指令的值被访问到：<code>v-slot:name=&quot;slotProps&quot;</code>。当使用缩写时是这样：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs template">&lt;MyComponent&gt;<br>  &lt;template #header=&quot;headerProps&quot;&gt;<br>    &#123;&#123; headerProps &#125;&#125;<br>  &lt;/template&gt;<br><br>  &lt;template #default=&quot;defaultProps&quot;&gt;<br>    &#123;&#123; defaultProps &#125;&#125;<br>  &lt;/template&gt;<br><br>  &lt;template #footer=&quot;footerProps&quot;&gt;<br>    &#123;&#123; footerProps &#125;&#125;<br>  &lt;/template&gt;<br>&lt;/MyComponent&gt;<br></code></pre></td></tr></table></figure>
                
              </div>
            
            <hr/>
            <div>
              <div class="post-metas my-3">
  
    <div class="post-meta mr-3 d-flex align-items-center">
      <i class="iconfont icon-category"></i>
      

<span class="category-chains">
  
  
    
      <span class="category-chain">
        
  <a href="/my-blog/categories/%E6%B5%8B%E8%AF%95%E5%BC%80%E5%8F%91/" class="category-chain-item">测试开发</a>
  
  
    <span>></span>
    
  <a href="/my-blog/categories/%E6%B5%8B%E8%AF%95%E5%BC%80%E5%8F%91/Vue/" class="category-chain-item">Vue</a>
  
  

  

      </span>
    
  
</span>

    </div>
  
  
    <div class="post-meta">
      <i class="iconfont icon-tags"></i>
      
        <a href="/my-blog/tags/%E6%B5%8B%E8%AF%95%E5%BC%80%E5%8F%91/">#测试开发</a>
      
        <a href="/my-blog/tags/%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0/">#测试平台</a>
      
        <a href="/my-blog/tags/javascript/">#javascript</a>
      
        <a href="/my-blog/tags/Vue/">#Vue</a>
      
    </div>
  
</div>


              
  

  <div class="license-box my-3">
    <div class="license-title">
      <div>vue组件</div>
      <div>https://walker_kwu.gitee.io/my-blog/2022/11/19/notes/vue/component/</div>
    </div>
    <div class="license-meta">
      
        <div class="license-meta-item">
          <div>作者</div>
          <div>木舟</div>
        </div>
      
      
        <div class="license-meta-item license-meta-date">
          <div>发布于</div>
          <div>2022年11月19日</div>
        </div>
      
      
        <div class="license-meta-item license-meta-date">
          <div>更新于</div>
          <div>2023年5月23日</div>
        </div>
      
      <div class="license-meta-item">
        <div>许可协议</div>
        <div>
          
            
            
              <a target="_blank" href="https://creativecommons.org/licenses/by-nc-sa/4.0/">
              <span class="hint--top hint--rounded" aria-label="BY - 署名">
                <i class="iconfont icon-by"></i>
              </span>
              </a>
            
              <a target="_blank" href="https://creativecommons.org/licenses/by-nc-sa/4.0/">
              <span class="hint--top hint--rounded" aria-label="NC - 非商业性使用">
                <i class="iconfont icon-nc"></i>
              </span>
              </a>
            
              <a target="_blank" href="https://creativecommons.org/licenses/by-nc-sa/4.0/">
              <span class="hint--top hint--rounded" aria-label="SA - 相同方式共享">
                <i class="iconfont icon-sa"></i>
              </span>
              </a>
            
          
        </div>
      </div>
    </div>
    <div class="license-icon iconfont"></div>
  </div>



              
                <div class="post-prevnext my-3">
                  <article class="post-prev col-6">
                    
                    
                      <a href="/my-blog/2022/11/20/notes/vue/attributes/" title="vue组件透传">
                        <i class="iconfont icon-arrowleft"></i>
                        <span class="hidden-mobile">vue组件透传</span>
                        <span class="visible-mobile">上一篇</span>
                      </a>
                    
                  </article>
                  <article class="post-next col-6">
                    
                    
                      <a href="/my-blog/2022/11/16/python/basic/base/" title="Python基础">
                        <span class="hidden-mobile">Python基础</span>
                        <span class="visible-mobile">下一篇</span>
                        <i class="iconfont icon-arrowright"></i>
                      </a>
                    
                  </article>
                </div>
              
            </div>

            
          </article>
        </div>
      </div>
    </div>

    <div class="side-col d-none d-lg-block col-lg-2">
      

    </div>
  </div>
</div>





  



  



  



  



  







    

    
      <a id="scroll-top-button" aria-label="TOP" href="#" role="button">
        <i class="iconfont icon-arrowup" aria-hidden="true"></i>
      </a>
    

    
      <div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="ModalLabel"
     aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">搜索</h4>
        <button type="button" id="local-search-close" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <input type="text" id="local-search-input" class="form-control validate">
          <label data-error="x" data-success="v" for="local-search-input">关键词</label>
        </div>
        <div class="list-group" id="local-search-result"></div>
      </div>
    </div>
  </div>
</div>

    

    
  </main>

  <footer>
    <div class="footer-inner">
  
    <div class="footer-content">
       <span>By 木舟</span> <i class="iconfont icon-love"></i> 
    </div>
  
  
    <div class="statistics">
  
  

  
    
      <span id="busuanzi_container_site_pv" style="display: none">
        总访问量 
        <span id="busuanzi_value_site_pv"></span>
         次
      </span>
    
    
      <span id="busuanzi_container_site_uv" style="display: none">
        总访客数 
        <span id="busuanzi_value_site_uv"></span>
         人
      </span>
    
    
  
</div>

  
  
  
</div>

  </footer>

  <!-- Scripts -->
  
  <script  src="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.js" ></script>
  <link  rel="stylesheet" href="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.css" />

  <script>
    NProgress.configure({"showSpinner":false,"trickleSpeed":100})
    NProgress.start()
    window.addEventListener('load', function() {
      NProgress.done();
    })
  </script>


<script  src="https://lib.baomitu.com/jquery/3.6.0/jquery.min.js" ></script>
<script  src="https://lib.baomitu.com/twitter-bootstrap/4.6.1/js/bootstrap.min.js" ></script>
<script  src="/my-blog/js/events.js" ></script>
<script  src="/my-blog/js/plugins.js" ></script>


  <script  src="https://lib.baomitu.com/typed.js/2.0.12/typed.min.js" ></script>
  <script>
    (function (window, document) {
      var typing = Fluid.plugins.typing;
      var subtitle = document.getElementById('subtitle');
      if (!subtitle || !typing) {
        return;
      }
      var text = subtitle.getAttribute('data-typed-text');
      
        typing(text);
      
    })(window, document);
  </script>




  
    <script  src="/my-blog/js/img-lazyload.js" ></script>
  




  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/tocbot/4.18.2/tocbot.min.js', function() {
    var toc = jQuery('#toc');
    if (toc.length === 0 || !window.tocbot) { return; }
    var boardCtn = jQuery('#board-ctn');
    var boardTop = boardCtn.offset().top;

    window.tocbot.init({
      tocSelector     : '#toc-body',
      contentSelector : '.markdown-body',
      headingSelector : CONFIG.toc.headingSelector || 'h1,h2,h3,h4,h5,h6',
      linkClass       : 'tocbot-link',
      activeLinkClass : 'tocbot-active-link',
      listClass       : 'tocbot-list',
      isCollapsedClass: 'tocbot-is-collapsed',
      collapsibleClass: 'tocbot-is-collapsible',
      collapseDepth   : CONFIG.toc.collapseDepth || 0,
      scrollSmooth    : true,
      headingsOffset  : -boardTop
    });
    if (toc.find('.toc-list-item').length > 0) {
      toc.css('visibility', 'visible');
    }
  });
</script>


  <script src=https://lib.baomitu.com/clipboard.js/2.0.10/clipboard.min.js></script>

  <script>Fluid.plugins.codeWidget();</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/anchor-js/4.3.1/anchor.min.js', function() {
    window.anchors.options = {
      placement: CONFIG.anchorjs.placement,
      visible  : CONFIG.anchorjs.visible
    };
    if (CONFIG.anchorjs.icon) {
      window.anchors.options.icon = CONFIG.anchorjs.icon;
    }
    var el = (CONFIG.anchorjs.element || 'h1,h2,h3,h4,h5,h6').split(',');
    var res = [];
    for (var item of el) {
      res.push('.markdown-body > ' + item.trim());
    }
    if (CONFIG.anchorjs.placement === 'left') {
      window.anchors.options.class = 'anchorjs-link-left';
    }
    window.anchors.add(res.join(', '));
  });
</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.js', function() {
    Fluid.plugins.fancyBox();
  });
</script>


  <script>Fluid.plugins.imageCaption();</script>

  <script  src="/my-blog/js/local-search.js" ></script>

  <script defer src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" ></script>





<!-- 主题的启动项，将它保持在最底部 -->
<!-- the boot of the theme, keep it at the bottom -->
<script  src="/my-blog/js/boot.js" ></script>


  

  <noscript>
    <div class="noscript-warning">博客在允许 JavaScript 运行的环境下浏览效果更佳</div>
  </noscript>
</body>
</html>
